package server

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"fromto"
	"io"
	"iso8583"
	"net"
	"strings"
	"sync"
	"time"
)

type ServerNode struct {
	listener     net.Listener
	serverAddr   *addressConf
	msgChan      chan bool
	templete     *iso8583.Iso
	ws           map[io.Writer]io.Writer
	isReadyMutex sync.Mutex
	packageId    uint64
	name         string
	startTime    time.Time
}

type packageStruct struct {
	Code   int    `code`
	Id     uint64 `packeageid`
	Raw    string `raw`
	Fields string `fields`
}

type packageError struct {
	Code int    `code`
	Msg  string `msg`
}

func NewServerNodeIso(conf ServerNodeConf) (*ServerNode, error) {
	address, template, err := conf.Get()
	if err != nil {
		return nil, err
	}

	li, err := net.Listen("tcp", ":0")
	if err != nil {
		return nil, err
	}

	iso, err := iso8583.New(template)
	if err != nil {
		li.Close()
		return nil, err
	}

	node := &ServerNode{
		listener:   li,
		serverAddr: address,
		msgChan:    make(chan bool, 1),
		templete:   iso,
		ws:         make(map[io.Writer]io.Writer),
		packageId:  0,
	}
	return node, nil
}

func NewServerNodeRaw(conf ServerNodeConf) (*ServerNode, error) {
	address, err := conf.GetAddress()
	if err != nil {
		return nil, err
	}

	li, err := net.Listen("tcp", ":0")
	if err != nil {
		return nil, err
	}

	node := &ServerNode{
		listener:   li,
		serverAddr: address,
		msgChan:    make(chan bool, 1),
		ws:         make(map[io.Writer]io.Writer),
		packageId:  0,
		name:       address.name}
	return node, nil
}

func (node *ServerNode) Local() (string, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return "", err
	}
	local := ""
	for _, a := range addrs {
		if inet, ok := a.(*net.IPNet); ok && !inet.IP.IsLoopback() && inet.IP.IsGlobalUnicast() {
			if inet.IP.To4() != nil {
				local = fmt.Sprintf("%s:%d", inet.IP.String(), node.listener.Addr().(*net.TCPAddr).Port)
			}
		}
	}

	return local, nil

	return local, nil
}
func (node *ServerNode) Id() (string, error) {
	local, err := node.Local()
	if err != nil {
		return "", err
	}
	return strings.Split(local, ":")[1], nil
}

func (node *ServerNode) DoIso() error {
	defer node.listener.Close()

	node.startTime = time.Now()

	var (
		closeNode bool = false
		conn      net.Conn
		err       error
	)

	for {
		conn, err = node.listener.Accept()
		if err != nil {
			return err
		}
		defer conn.Close()
		select {
		case closeNode = <-node.msgChan:
		default:
		}
		if closeNode {
			close(node.msgChan)
			node.WriteError("[[节点关闭]]", errors.New("请刷新浏览器新建节点"))
			return nil
		}

		go func(conn net.Conn) {
			defer conn.Close()
			cli, err := fromto.New(node.serverAddr.ip+":"+node.serverAddr.port, conn)
			if err != nil {
				node.WriteError("CONNECT", err)
				return
			}

			err = cli.RecvFromPos()
			if err != nil {
				node.WriteError("RECV FROM POS", err)
				return
			}
			send, _ := cli.GetData()
			node.WriteIso("SEND", send)

			//
			//
			err = cli.Connect()
			if err != nil {
				node.WriteError("CONNECT TO SERVER", err)
				return
			}

			defer cli.Close()
			err = cli.SendToServer()
			if err != nil {
				node.WriteError("SEND TO SERVER", err)
				return
			}

			//
			//
			err = cli.RecvFromServer()
			if err != nil {
				node.WriteError("RECV FROM SERVER", err)
				return
			}
			_, recv := cli.GetData()
			node.WriteIso("RECV", recv)

			err = cli.SendToPos()
			if err != nil {
				node.WriteError("SEND TO POS", err)
				return
			}
			node.isReadyMutex.Lock()
			defer node.isReadyMutex.Unlock()
			node.packageId++
		}(conn)
	}
}

func (node *ServerNode) DoRaw() error {
	defer node.listener.Close()

	node.startTime = time.Now()
	fmt.Println("start time:", node.startTime)

	var (
		closeNode bool = false
	)
	for {
		conn, err := node.listener.Accept()
		if err != nil {
			return err
		}
		select {
		case closeNode = <-node.msgChan:
		default:
		}
		if closeNode {
			fmt.Println("close node")
			close(node.msgChan)
			node.WriteError("[[节点关闭]]", errors.New("请刷新浏览器新建节点"))
			return nil
		}
		//
		//
		go func(conn net.Conn) {
			defer conn.Close()
			cli, err := fromto.New(node.serverAddr.ip+":"+node.serverAddr.port, conn)
			if err != nil {
				node.WriteError("CONNECT", err)
				return
			}

			//
			//
			err = cli.RecvFromPos()
			if err != nil {
				node.WriteError("RECV FROM POS", err)
				return
			}
			send, _ := cli.GetData()

			node.WriteRaw("SEND", send)

			//
			//
			err = cli.Connect()
			if err != nil {
				node.WriteError("CONNECT TO SERVER", err)
				return
			}
			defer cli.Close()
			//
			//
			err = cli.SendToServer()
			if err != nil {
				node.WriteError("SEND TO SERVER", err)
				return
			}

			//
			//
			err = cli.RecvFromServer()
			if err != nil {
				node.WriteError("RECV FROM SERVER", err)
				return
			}
			_, recv := cli.GetData()
			node.WriteRaw("RECV", recv)

			err = cli.SendToPos()
			if err != nil {
				node.WriteError("SEND TO POS", err)
				return
			}
			node.isReadyMutex.Lock()
			defer node.isReadyMutex.Unlock()
			node.packageId++
		}(conn)

	}
}

func (node *ServerNode) Close() error {
	node.msgChan <- true
	local, err := node.Local()
	if err != nil {
		return errors.New("close error!")
	}
	conn, err := net.Dial("tcp", local)
	if err != nil {
		return errors.New("close error!")
	}
	conn.Close()
	return nil
}

func (node *ServerNode) WriteError(title string, err error) {
	var js map[string]interface{} = make(map[string]interface{})

	js["type"] = "error"
	js["code"] = "-1"
	js["title"] = title
	js["error"] = err.Error()

	output, _ := json.Marshal(&js)

	node.isReadyMutex.Lock()
	defer node.isReadyMutex.Unlock()

	for _, ws := range node.ws {
		ws.Write(output)
	}
}

func (node *ServerNode) WriteRaw(title string, raw []byte) {
	var js map[string]interface{} = make(map[string]interface{})

	js["type"] = "raw"
	js["code"] = "0"
	js["id"] = node.packageId
	js["title"] = title
	js["raw"] = hex.EncodeToString(raw)
	js["raw_len"] = len(raw)

	output, _ := json.Marshal(&js)
	node.isReadyMutex.Lock()
	defer node.isReadyMutex.Unlock()

	for _, ws := range node.ws {
		ws.Write(output)
	}

}

func (node *ServerNode) WriteIso(title string, raw []byte) {
	var js map[string]interface{} = make(map[string]interface{})

	if len(raw) <= 13 {
		node.WriteRaw(title, raw)
		return
	}
	result, err := node.templete.Unmarshal(bytes.NewReader(raw[13:]))
	if err != nil {
		js["type"] = "iso"
		js["code"] = "0"
		js["id"] = node.packageId
		js["title"] = title
		js["error"] = err.Error()
		js["raw"] = hex.EncodeToString(raw)
		js["raw_len"] = len(raw)
	} else {
		iso := ""

		iso += fmt.Sprintf("[msg]%s\n", hex.EncodeToString(result.Get(iso8583.MSG)))
		iso += fmt.Sprintf("[bitmap]%s\n", hex.EncodeToString(result.Get(iso8583.BITMAP)))

		for i := 1; i <= 64; i++ {
			if v := result.Get(i); v != nil {
				iso += fmt.Sprintf("[%d]%s\n", i, hex.EncodeToString(result.Get(i)))
			}
		}
		js["type"] = "iso"
		js["code"] = "0"
		js["id"] = node.packageId
		js["title"] = title
		js["raw"] = hex.EncodeToString(raw)
		js["raw_len"] = len(raw)
		js["iso"] = iso
	}

	output, _ := json.Marshal(&js)

	node.isReadyMutex.Lock()
	defer node.isReadyMutex.Unlock()

	for _, ws := range node.ws {
		ws.Write(output)
	}
}
